// (C) 2022 Intel Corporation. All rights reserved.
// Your use of Intel Corporation's design tools, logic functions and other 
// software and tools, and its AMPP partner logic functions, and any output 
// files from any of the foregoing (including device programming or simulation 
// files), and any associated documentation or information are expressly subject 
// to the terms and conditions of the Intel Program License Subscription 
// Agreement, Intel FPGA IP License Agreement, or other applicable 
// license agreement, including, without limitation, that your use is for the 
// sole purpose of programming logic devices manufactured by Intel and sold by 
// Intel or its authorized distributors.  Please refer to the applicable 
// agreement for further details.

module i2c_slv
(
  input              Clock,                   //FPGA Primary Synchronous Clock
  input              Reset,                   //Synchronous Reset
  input              SclIn,                   //I2C Clock Input value
  input              SdaIn,                   //I2C Data Input value
  output reg         SdaOut,                  //SDA Data Output value, when lo will enable open drain output    
  input      [ 7: 0] DeviceAddress,           //The 8-bit address for this device on I2C
  //avmm intf
  output     [15: 0] avmm_address,            //Write/Read Address for writing to registers
  output     [15: 0] avmm_writedata,          //Write Data for writing to registers
  output reg         avmm_write,              //Write Enable for marking Address and Data as valid to write
  output reg         avmm_read,               
  input              avmm_readvalid,
  input      [15: 0] avmm_readdata,            //Read Data for reading from registers  
 
  output             I2C_CRC_Error
);

  reg     [7: 0] PreviousWrImageCrc;
  reg             NextCrcError;
  reg             CrcWait;
  reg     [7: 0] WrImageCrcVal;
  wire    [7: 0] NewWrImageCrcVal;
  reg             set_NextCrcError;
  reg             clear_NextCrcError;
  //change ports to internal reg. Delete image crc function
  reg             I2cError;                //Error with communication
  reg             WatchdogTimeout;         //Communication has failed, reset controls to default state                               
  reg             CrcError;
  reg [16: 0]     FlashOpAddr;
  reg [7: 0]     CurrentWrImageCrc;
  //Internal Wires
  wire            SclRise;                  //Detected rising edge of SCL
  wire            SclFall;                  //Detected falling edge of SCL
  wire            SdaRise;                  //Detected rising edge of SDA
  wire            SdaFall;                  //Detected falling edge of SDA
  wire            DetectStart;              //Detected start condition for I2C
  wire            DetectStop;               //Detected stop condition for I2C
  wire            AddressMatch;             //The device address matches the I2C device address byte
  wire            WrFifoWe;                 //Write enable for write FIFO to store incoming data
  wire    [7: 0] NewWrCrcVal;              //Computed next CRC value
  wire    [7: 0] NewRdCrcVal;              //Computed next CRC value
  wire            I2cErrorDetect;           //An error with this transmission occurred
  reg             BytePrep;                 //Clear registers to ready for new byte incoming
  reg             ByteStoreBits;            //Enable storing bits on rising SCL edges
  reg             CheckAddress;             //Mode is valid for checking the device address byte
  reg             ByteDone;                 //An operation packet byte or normal byte has completed
  reg             CrcClr;                   //Resets CRC calculated value to initial value for new packet
  reg             NextSdaOut;               //Computed next value

  reg             NextI2cError;             //Computed next value
  reg             ByteLoaded;               //A new byte has been loaded for output and needs CRC calculation
  reg             ByteLoadMsb;              //Load the MSB into output register
  reg             ByteLoadLsb;              //Load the LSB into output register
  reg             ByteLoadCrcM;             //Load the CRC MSB into output register
  reg             ByteLoadCrcL;             //Load the CRC LSB into output register
  reg             ByteReadBits;             //Allow shifting bits out on falling edge of SCL
  reg             SetTimeoutCnt;            //Set the timeout counter
  reg             CrcMsbDone;               //The CRC MSB byte is completed on receive
  reg             WatchdogRst;              //Reset the Watchdog timer since a read completed
  
  //Internal Registers
  reg             PrevSclIn;                //Previous value for basic glitch prevention
  reg             PrevSdaIn;                //Previous value for basic glitch prevention
  reg             Scl;                      //Deglitched value of SCL input
  reg             Sda;                      //Deglitched value of SCL input
  reg             PrevScl;                  //Previous value
  reg             PrevSda;                  //Previous value
  reg     [ 7: 0] CurrentByteVal;           //Value of current I2C byte, shifted in MSB first
  reg             OpIsRd;                   //Direction for I2C operation
  reg     [15: 0] OpAddr;                   //Register address from I2C operation
  reg     [ 7: 0] CurrentByteId;            //Byte position within I2C operation
  reg     [ 7: 0] OpNumBytes;               //Number of bytes to expect in I2C operation payload
  reg     [ 7: 0] WordMsb;                  //Upper 8 bits of the word being transfered
  reg     [ 7: 0] WrCrcVal;                 //Computed CRC value of incoming data
  reg     [ 7: 0] RdCrcVal;                 //Computed CRC value of outgoing data
  reg     [ 3: 0] BitCnt;                   //Bit counter for tracking byte completion
  reg     [31: 0] TimeoutCnt;               //Timeout counter in case we get stuck
  reg     [25: 0] WatchdogCnt;              //Watchdog counter for making sure we have valid communication from master
  
  
  assign I2C_CRC_Error   =  I2cError || CrcError;
  //Counter to make sure the master is still giving us information
  always @(posedge Clock)
  begin
    if(Reset)
      WatchdogCnt <= 26'b0;
    else if(WatchdogRst)
      WatchdogCnt <= 26'd50000000; //5.0s
    else if(WatchdogCnt != 26'b0)
      WatchdogCnt <= WatchdogCnt - 26'd1;
    else
      WatchdogCnt <= WatchdogCnt;
  end
  
  //Indicate when the watchdog timer has not been reset
  always @(posedge Clock)
  begin
    if(Reset)
      WatchdogTimeout <= 1'b0;
    else if(WatchdogCnt == 26'b0)
      WatchdogTimeout <= 1'b1;
    else
      WatchdogTimeout <= 1'b0;
  end
  
  //Counter to track a timeout
  always @(posedge Clock)
  begin
    if(Reset)
      TimeoutCnt <= 32'b0;
    else if(SetTimeoutCnt)
      TimeoutCnt <= 32'd500000;
    else if(TimeoutCnt != 32'b0)
      TimeoutCnt <= TimeoutCnt - 32'd1;
    else
      TimeoutCnt <= TimeoutCnt;
  end
  
  //Store previous value and only change output when they match
  always @(posedge Clock)
  begin
    if(Reset)
      begin
        PrevSclIn <= 1'b0;
        Scl       <= 1'b0;
      end
    else if(SclIn & PrevSclIn)
      begin
        //Matches previous value, update
        PrevSclIn <= SclIn;
        Scl       <= SclIn;
      end
    else if(!SclIn & !PrevSclIn)
      begin
        //Matches previous value, update
        PrevSclIn <= SclIn;
        Scl       <= SclIn;
      end
    else
      begin
        //Does not match, leave output unchanged
        PrevSclIn <= SclIn;
        Scl       <= Scl;
      end
  end
  
  //Store previous value and only change output when they match
  always @(posedge Clock)
  begin
    if(Reset)
      begin
        PrevSdaIn <= 1'b0;
        Sda       <= 1'b0;
      end
    else if(SdaIn & PrevSdaIn)
      begin
        //Matches previous value, update
        PrevSdaIn <= SdaIn;
        Sda       <= SdaIn;
      end
    else if(!SdaIn & !PrevSdaIn)
      begin
        //Matches previous value, update
        PrevSdaIn <= SdaIn;
        Sda       <= SdaIn;
      end
    else
      begin
        //Does not match, leave output unchanged
        PrevSdaIn <= SdaIn;
        Sda       <= Sda;
      end
  end
  
  //Store previous values of SCL and SDA to be able to detect changes
  always @(posedge Clock)
  begin
    if(Reset)
      begin
        PrevScl <= 1'b0;
        PrevSda <= 1'b0;
      end
    else
      begin
        PrevScl <= Scl;
        PrevSda <= Sda;
      end
  end
  
  //Look for rising edges and falling edges
  assign SclRise = (Scl & !PrevScl);
  assign SclFall = (!Scl & PrevScl);
  
  //Look for rising edges and falling edges
  assign SdaRise = (Sda & !PrevSda);
  assign SdaFall = (!Sda & PrevSda);
  
  //Start condition is when SDA goes lo while SCL is hi
  assign DetectStart = (SdaFall & Scl);
  
  //Stop condition is when SDA goes hi while SCL is hi
  assign DetectStop = (SdaRise & Scl);
  
  //Store the incoming byte when we are ready for it, skip the ACK, MSB first
  always @(posedge Clock)
  begin
    if(Reset)
      CurrentByteVal <= 8'b0;
    else if(BytePrep)
      CurrentByteVal <= 8'b0;
    else if(SclRise & ByteStoreBits)
      //New bit for a written value
      CurrentByteVal <= {CurrentByteVal[6:0], Sda};
    else if(ByteLoadMsb)
      //Load MSB for a read value
      CurrentByteVal <= avmm_readdata[15:8];
    else if(ByteLoadLsb)
      //Load LSB for a read value
      CurrentByteVal <= avmm_readdata[7:0];
    else if(ByteLoadCrcL)
      //Load CRC for final read value
      CurrentByteVal <= RdCrcVal[7:0];
    else if(SclFall & ByteReadBits)
      //Shift off the MSB
      CurrentByteVal <= {CurrentByteVal[6:0], 1'b0};
    else
      CurrentByteVal <= CurrentByteVal;
  end
  
  //Determine if current address matches our address (ignore bit 0 since it is R/W)
  assign AddressMatch = (CurrentByteVal[7:1] == DeviceAddress[7:1]);
  
  //Store the type of operation
  always @(posedge Clock)
  begin
    if(Reset)
      OpIsRd <= 1'b0;
    else if(CheckAddress & AddressMatch)
      OpIsRd <= CurrentByteVal[0];
    else
      OpIsRd <= OpIsRd;
  end
  
  //Store what byte we are on in the message
  always @(posedge Clock)
  begin
    if(Reset)
      CurrentByteId <= 8'b0;
    else if(CheckAddress)
      //Reset after the address byte
      CurrentByteId <= 8'b0;
    else if(ByteDone)
      //Increment for a byte on write
      CurrentByteId <= CurrentByteId + 8'd1;
    else if(avmm_write)
      //Decrement two for a write to Reg File
      CurrentByteId <= CurrentByteId - 8'd2;
    else if(ByteLoadMsb | ByteLoadLsb)
      //Increment for a byte on red
      CurrentByteId <= CurrentByteId + 8'd1;
    else
      CurrentByteId <= CurrentByteId;
  end
  
  //Store the starting address
  always @(posedge Clock)
  begin
    if(Reset)
      OpAddr <= 16'b0;
    else if(ByteDone & (CurrentByteId == 8'd0))
      //Store the MSB
      OpAddr <= {CurrentByteVal, 8'b0};
    else if(ByteDone & (CurrentByteId == 8'd1))
      //Store the LSB
      OpAddr <= {OpAddr[15:8], CurrentByteVal};
    else if(avmm_write)
      //Write going out to Reg File
      OpAddr <= OpAddr + 16'd1;
    else if(ByteLoadLsb)
      //Completed a read from Reg File
      OpAddr <= OpAddr + 16'd1;
    else
      OpAddr <= OpAddr;
  end
  
  //Use the operation address as the Reg File address for both writing and reading
  assign avmm_address = OpAddr;
  
  //Store the number of words
  always @(posedge Clock)
  begin
    if(Reset)
      OpNumBytes <= 8'b0;
    else if(ByteDone & (CurrentByteId == 8'd2))
      //The MSB cannot actually be used, since our max length is 100 words
      OpNumBytes <= 8'b0;
    else if(ByteDone & (CurrentByteId == 8'd3))
      //Bytes is twice the number of words, store LSB
      OpNumBytes <= {CurrentByteVal[6:0], 1'b0};
    else
      OpNumBytes <= OpNumBytes;
  end
  
  //Store the previous byte so we have the MSB to go with the LSB
  always @(posedge Clock)
  begin
    if(Reset)
      WordMsb <= 8'b0;
    else if(ByteDone | CrcMsbDone)
      WordMsb <= CurrentByteVal;
    else
      WordMsb <= WordMsb;
  end
  
  //Write to storage RAM when it is the data section, a write and the second byte
  assign WrFifoWe = (!OpIsRd & ByteDone & (CurrentByteId >= 5) & CurrentByteId[0]);
  
  //FIFO for holding writes before CRC is checked
  I2C_SLAVE_WR_FIFO I2cWrFifo
    (
      .clock                        (Clock                        ),
      .wrreq                        (WrFifoWe                     ),
      .data                         ({WordMsb, CurrentByteVal}    ),
      .rdreq                        (avmm_write                   ),
      .q                            (avmm_writedata               )
    );
  
  //CRC computation module
  CRC8_D8 CrcModuleWr
    (
      .Data                         (CurrentByteVal               ),
      .Crc                          (WrCrcVal                     ),
      .NewCrc                       (NewWrCrcVal                  )
    );
      
  //Store updated CRC whenever valid
  always @(posedge Clock)
  begin
    if(Reset)
      WrCrcVal <= 8'b0;
    else if(CrcClr)
      //Initial starting value
      WrCrcVal <= 8'h00;
    else if(ByteDone | CheckAddress)
      //CRC includes the address byte, the op bytes, and the normal bytes
      WrCrcVal <= NewWrCrcVal;
    else
      WrCrcVal <= WrCrcVal;
  end
  
    //CRC computation module
  CRC8_D8 CrcModuleWrImage
    (
      .Data                         (CurrentByteVal               ),
      .Crc                          (WrImageCrcVal                ),
      .NewCrc                       (NewWrImageCrcVal             )
    );
     
  //Store updated CRC whenever valid
  always @(posedge Clock)
  begin
    if(Reset)
      WrImageCrcVal <= 8'b0;
    else if(CrcClr)                                                                                                                                            
      //Initial starting value
      WrImageCrcVal <= 8'h00;    
    else if((OpAddr[15:8] == 8'b00000001) & ByteDone & (CurrentByteId == 8'd3))                                                                            
      WrImageCrcVal <= CurrentWrImageCrc;     
     else if(ByteDone | CheckAddress)
      //CRC includes the address byte, the op bytes, and the normal bytes
      WrImageCrcVal <= NewWrImageCrcVal;
    else
      WrImageCrcVal <= WrImageCrcVal;
  end
  
  always @(*) begin
        FlashOpAddr <= 'b0;
  end
  
  always @(posedge Clock)
  begin
    if((FlashOpAddr == 17'b0) & (OpAddr[15:8] == 8'b00000001) & ByteDone & (CurrentByteId == 8'd2))
     begin
      CurrentWrImageCrc  <= 8'h00;
     end
     else if((OpAddr[15:8] == 8'b00000001) & (CrcWait == 1'b1))
        CurrentWrImageCrc <= WrImageCrcVal;
     else if((OpAddr[15:8] == 8'b00000001) & (NextCrcError == 1'b1))
        CurrentWrImageCrc <= PreviousWrImageCrc;
     else
     begin
        CurrentWrImageCrc <= CurrentWrImageCrc;
     end
  end
    
  always @(posedge Clock)
  begin
    if((FlashOpAddr == 17'b0) & (OpAddr[15:8] == 8'b00000001) & ByteDone & (CurrentByteId == 8'd2))
     begin
        PreviousWrImageCrc <= 8'h00;
     end
     else if((OpAddr[15:8] == 8'b00000001) & (NextCrcError == 1'b0) & (avmm_write == 1'b1))
        PreviousWrImageCrc <= CurrentWrImageCrc;
     else
     begin
        PreviousWrImageCrc <= PreviousWrImageCrc;
     end
  end    
    
      
 //Store updated CRCError
 always @(posedge Clock)
  begin
    if(Reset)
      CrcError <= 1'b0;                                                                                                                                    
    else 
      CrcError <= NextCrcError;
  end
  
  always @ (posedge Clock) //add to delete latch
  begin
    if(Reset)
       NextCrcError <= 1'b0;
    else if (set_NextCrcError)
       NextCrcError <= 1'b1;
    else if (clear_NextCrcError)
       NextCrcError <= 1'b0;
    else
       NextCrcError <= NextCrcError;
  end

  
  //Count number of bits on received byte
  always @(posedge Clock)
  begin
    if(Reset)
      BitCnt <= 4'b0;
    else if(BytePrep)
      //Clear for counting on a write
      BitCnt <= 4'b0;
    else if(ByteLoadMsb | ByteLoadLsb | ByteLoadCrcM | ByteLoadCrcL)
      //Clear for counting on a read
      BitCnt <= 4'b0;
    else if(SclRise & ByteStoreBits)
      //Bit advanced on write
      BitCnt <= BitCnt + 4'd1;
    else if(SclFall & ByteReadBits)
      //Bit advanced on read
      BitCnt <= BitCnt + 4'd1;
    else
      BitCnt <= BitCnt;
  end
  
  //CRC computation module
  CRC8_D8 CrcModuleRd
    (
      .Data                         (CurrentByteVal               ),
      .Crc                          (RdCrcVal                     ),
      .NewCrc                       (NewRdCrcVal                  )
    );
      
  //Store updated CRC whenever valid
  always @(posedge Clock)
  begin
    if(Reset)
      RdCrcVal <= 8'b0;
    else if(CrcClr)
      //Initial starting value
      RdCrcVal <= 8'h00;
    else if(ByteDone | CheckAddress | ByteLoaded)
      //CRC includes the address byte, the op bytes, and the normal bytes
      RdCrcVal <= NewRdCrcVal;
    else
      RdCrcVal <= RdCrcVal;
  end
  
  //State Machine to control incoming data
  parameter [5:0]
    I2S_IDLE              =  0,
    
    I2S_ADDR_GET_BITS     =  1,
    I2S_ADDR_CHK          =  2,
    I2S_ADDR_WAIT_FE      =  3,
    I2S_ADDR_ACK          =  4,
    
    I2S_W_BYTE_PREP       =  5,
    I2S_W_BYTE_GET_BITS   =  6,
    I2S_W_BYTE_STORE      =  7,
    I2S_W_BYTE_WAIT_FE    =  8,
    I2S_W_BYTE_ACK        =  9,
    
    I2S_W_CRC_M_PREP      = 10,
    I2S_W_CRC_M_GET_BITS  = 11,
    I2S_W_CRC_M_STORE     = 12,
    I2S_W_CRC_M_WAIT_FE   = 13,
    I2S_W_CRC_M_ACK       = 14,
    I2S_W_CRC_L_PREP      = 15,
    I2S_W_CRC_L_GET_BITS  = 16,
    I2S_W_CRC_L_WAIT_FE   = 17,
    I2S_W_CRC_L_ACK       = 18,
    
    I2S_W_REG_WR          = 19,
    
    I2S_R_MSB_LOAD        = 20,
    I2S_R_MSB_CRC         = 21,
    I2S_R_MSB_SEND_BITS   = 22,
    I2S_R_MSB_WAIT_FE     = 23,
    I2S_R_LSB_LOAD        = 24,
    I2S_R_LSB_CRC         = 25,
    I2S_R_LSB_SEND_BITS   = 26,
    I2S_R_LSB_WAIT_FE     = 27,
    
    I2S_R_CRC_M_LOAD      = 28,
    I2S_R_CRC_M_SEND_BITS = 29,
    I2S_R_CRC_M_WAIT_FE   = 30,
    I2S_R_CRC_L_LOAD      = 31,
    I2S_R_CRC_L_SEND_BITS = 32,
    I2S_R_CRC_L_WAIT_FE   = 33,
    
    I2S_ERROR             = 34,
    I2S_CRC_ERROR         = 35,
    
    I2S_A_RST             = 36,
    I2S_A_GET_ADDR        = 37,
    I2S_A_ADDR_CHK        = 38,
    I2S_A_ADDR_WAIT_FE    = 39,
    I2S_A_ADDR_ACK        = 40;
    
  reg [5:0] CurrentState, NextState;
  
  always @(posedge Clock)
  begin
    if(Reset)
      CurrentState <= I2S_IDLE;
    else
      CurrentState <= NextState;
  end
  
  //An I2C Error is an unexpected stop or a timeout
  assign I2cErrorDetect = (DetectStop | (TimeoutCnt == 32'b0));
  
  //Next state logic
  always @(CurrentState or DetectStart or DetectStop or BitCnt or AddressMatch or SclFall or CurrentByteId or OpNumBytes or WrCrcVal or CurrentByteVal or OpIsRd or I2cErrorDetect or WordMsb or NextCrcError)
  begin
    case(CurrentState)
    
      I2S_IDLE :
        begin
          //Stay here until a Start happens
          if(DetectStart)
            NextState <= I2S_ADDR_GET_BITS;
          else
            NextState <= I2S_IDLE;
        end
        
      /////////////////////////////////////////////////////////////////////////////
      // Get the I2C Device Address Byte
      /////////////////////////////////////////////////////////////////////////////
        
      I2S_ADDR_GET_BITS :
        begin
          if(I2cErrorDetect)
            //If we see an error we should terminate
            NextState <= I2S_ERROR;
          else if(BitCnt == 4'd8)
            //Stay here until all 8 normal bits are received
            NextState <= I2S_ADDR_CHK;
          else
            NextState <= I2S_ADDR_GET_BITS;
        end
        
      I2S_ADDR_CHK :
        begin
          if(I2cErrorDetect)
            //If we see an error we should terminate
            NextState <= I2S_ERROR;
          else if(AddressMatch)
            //If the address matches we will ACK, otherwise ignore it
            NextState <= I2S_ADDR_WAIT_FE;
          else
            NextState <= I2S_IDLE;
        end
      
      I2S_ADDR_WAIT_FE :
        begin
          if(I2cErrorDetect)
            //If we see an error we should terminate
            NextState <= I2S_ERROR;
          else if(SclFall)
            //Wait for the falling edge so we can set the ACK bit
            NextState <= I2S_ADDR_ACK;
          else
            NextState <= I2S_ADDR_WAIT_FE;
        end
      
      I2S_ADDR_ACK :
        begin
          if(I2cErrorDetect)
            //If we see an error we should terminate
            NextState <= I2S_ERROR;
          else if(SclFall)
            //Do ACK until the next falling edge
            NextState <= I2S_W_BYTE_PREP;
          else
            NextState <= I2S_ADDR_ACK;
        end
      
      /////////////////////////////////////////////////////////////////////////////
      // Get the op bytes and all normal bytes
      /////////////////////////////////////////////////////////////////////////////
        
      I2S_W_BYTE_PREP :
        begin
          if(I2cErrorDetect)
            //If we see an error we should terminate
            NextState <= I2S_ERROR;
          else
            //Move on to get a byte
            NextState <= I2S_W_BYTE_GET_BITS;
        end
      
      I2S_W_BYTE_GET_BITS :
        begin
          if(I2cErrorDetect) //If we see an error we should terminate
            NextState <= I2S_ERROR;
          else if (DetectStart) //Switch to Read branch if a ReStart is detected.
            NextState <= I2S_A_RST;
          else if(BitCnt == 4'd8)//Stay here until all 8 normal bits are received
            NextState <= I2S_W_BYTE_STORE;
          else
            NextState <= I2S_W_BYTE_GET_BITS;
        end
      /////////////////////////////////////////////////////////////////////////////
      // Switch to Read Branch
      /////////////////////////////////////////////////////////////////////////////        
      I2S_A_RST:
        begin
          //Stay here until a Start happens
          if(I2cErrorDetect)
            NextState <= I2S_ERROR;
          else
            NextState <= I2S_A_GET_ADDR;
        end
        
      I2S_A_GET_ADDR:
        begin
          if(I2cErrorDetect)
            //If we see an error we should terminate
            NextState <= I2S_ERROR;
          else if(BitCnt == 4'd8)
            //Stay here until all 8 normal bits are received
            NextState <= I2S_A_ADDR_CHK;
          else
            NextState <= I2S_A_GET_ADDR;
        end
        
      I2S_A_ADDR_CHK:
        begin
          if(I2cErrorDetect)
            //If we see an error we should terminate
            NextState <= I2S_ERROR;
          else if(AddressMatch)
            //If the address matches we will ACK, otherwise ignore it
            NextState <= I2S_A_ADDR_WAIT_FE;
          else
            NextState <= I2S_IDLE;
        end
        
      I2S_A_ADDR_WAIT_FE:
        begin
          if(I2cErrorDetect)
            //If we see an error we should terminate
            NextState <= I2S_ERROR;
          else if(SclFall)
            //Wait for the falling edge so we can set the ACK bit
            NextState <= I2S_A_ADDR_ACK;
          else
            NextState <= I2S_A_ADDR_WAIT_FE;
        end
        
      I2S_A_ADDR_ACK:
        begin
          if(I2cErrorDetect)
            //If we see an error we should terminate
            NextState <= I2S_ERROR;
          else if(SclFall & OpIsRd & (CurrentByteId >= 8'd0))
            //Do ACK until the next falling edge
            NextState <= I2S_R_MSB_LOAD;
          else
            NextState <= I2S_A_ADDR_ACK;
        end
        
      I2S_W_BYTE_STORE :
        begin
          if(I2cErrorDetect)
            //If we see an error we should terminate
            NextState <= I2S_ERROR;
          else
            //Move on to get a byte
            NextState <= I2S_W_BYTE_WAIT_FE;
        end
      
      I2S_W_BYTE_WAIT_FE :
        begin
          if(I2cErrorDetect)
            //If we see an error we should terminate
            NextState <= I2S_ERROR;
          else if(SclFall)
            //Wait for the falling edge so we can set the ACK bit
            NextState <= I2S_W_BYTE_ACK;
          else
            NextState <= I2S_W_BYTE_WAIT_FE;
        end
      
      I2S_W_BYTE_ACK :
        begin
          if(I2cErrorDetect)
            //If we see an error we should terminate
            NextState <= I2S_ERROR;
          else if(SclFall & (CurrentByteId >= 8'd6) & ((CurrentByteId - 8'd4) >= OpNumBytes))
            //Done with all normal bytes, go wait for CRC byte
           NextState <= I2S_W_CRC_L_PREP;
          else if(SclFall)
            //Do another byte since we aren't done yet
            NextState <= I2S_W_BYTE_PREP;
          else
            NextState <= I2S_W_BYTE_ACK;
        end
      
      /////////////////////////////////////////////////////////////////////////////
      // Get the CRC word, MSB first
      /////////////////////////////////////////////////////////////////////////////
        
      I2S_W_CRC_M_PREP :
        begin
          if(I2cErrorDetect)
            //If we see an error we should terminate
            NextState <= I2S_ERROR;
          else
            //Move on to get a byte
            NextState <= I2S_W_CRC_M_GET_BITS;
        end
      
      I2S_W_CRC_M_GET_BITS :
        begin
          if(I2cErrorDetect)
            //If we see an error we should terminate
            NextState <= I2S_ERROR;
          else if(BitCnt == 4'd8)
            //Stay here until all 8 normal bits are received
            NextState <= I2S_W_CRC_M_STORE;
          else
            NextState <= I2S_W_CRC_M_GET_BITS;
        end
        
      I2S_W_CRC_M_STORE :
        begin
          if(I2cErrorDetect)
            //If we see an error we should terminate
            NextState <= I2S_ERROR;
          else
            //Move on to get a byte
            NextState <= I2S_W_CRC_M_WAIT_FE;
        end
        
      I2S_W_CRC_M_WAIT_FE :
        begin
          if(I2cErrorDetect)
            //If we see an error we should terminate
            NextState <= I2S_ERROR;
          else if(SclFall)
            //Always ack the MSB
            NextState <= I2S_W_CRC_M_ACK;
          else
            NextState <= I2S_W_CRC_M_WAIT_FE;
        end
      
      I2S_W_CRC_M_ACK :
        begin
          if(I2cErrorDetect)
            //If we see an error we should terminate
            NextState <= I2S_ERROR;
          else if(SclFall)
            //Done with ACK go on
            NextState <= I2S_W_CRC_L_PREP;
          else
            NextState <= I2S_W_CRC_M_ACK;
        end
        
      I2S_W_CRC_L_PREP :
        begin
          if(I2cErrorDetect)
            //If we see an error we should terminate
            NextState <= I2S_ERROR;
          else
            //Move on to get a byte
            NextState <= I2S_W_CRC_L_GET_BITS;
        end
      
      I2S_W_CRC_L_GET_BITS :
        begin
          if(I2cErrorDetect)
            //If we see an error we should terminate
            NextState <= I2S_ERROR;
          else if(BitCnt == 4'd8)
            //Stay here until all 8 normal bits are received
            NextState <= I2S_W_CRC_L_WAIT_FE;
          else
            NextState <= I2S_W_CRC_L_GET_BITS;
        end
        
      I2S_W_CRC_L_WAIT_FE :
        begin
          if(I2cErrorDetect)
            //If we see an error we should terminate
            NextState <= I2S_ERROR;    
				else if(SclFall & (WrCrcVal == {CurrentByteVal}))
            //CRC matches, go ahead and ACK
            NextState <= I2S_W_CRC_L_ACK;
          else if(SclFall)
            NextState <= I2S_CRC_ERROR;
          else
            NextState <= I2S_W_CRC_L_WAIT_FE;
        end
      
      I2S_W_CRC_L_ACK :
        begin
          if(I2cErrorDetect)
            //If we see an error we should terminate
            NextState <= I2S_ERROR;
          else if(SclFall && (NextCrcError == 1'b0))
            //Done with ACK go on
                begin
            NextState <= I2S_W_REG_WR;
                end
             else if(SclFall && (NextCrcError == 1'b1))
                NextState <= I2S_IDLE;
          else
            NextState <= I2S_W_CRC_L_ACK;
        end
        
      /////////////////////////////////////////////////////////////////////////////
      // Write data to register file
      /////////////////////////////////////////////////////////////////////////////
        
      I2S_W_REG_WR :
        begin
          if(CurrentByteId <= 8'd6)
            //Stay here until write to regs complete (four for Addr + NumWords, two for the first byte pair)
            NextState <= I2S_IDLE;
          else
            NextState <= I2S_W_REG_WR;
        end
      
      /////////////////////////////////////////////////////////////////////////////
      // Read operation - send two bytes per word
      /////////////////////////////////////////////////////////////////////////////
        
      I2S_R_MSB_LOAD :
        begin
          if(I2cErrorDetect)
            //If we see an error we should terminate
            NextState <= I2S_ERROR;
          else
            NextState <= I2S_R_MSB_CRC;
        end
      
      I2S_R_MSB_CRC :
        begin
          if(I2cErrorDetect)
            //If we see an error we should terminate
            NextState <= I2S_ERROR;
          else
            NextState <= I2S_R_MSB_SEND_BITS;
        end
      
      I2S_R_MSB_SEND_BITS :
        begin
          if(I2cErrorDetect)
            //If we see an error we should terminate
            NextState <= I2S_ERROR;
          else if(BitCnt == 4'd8)
            //Stay here until all 8 normal bits are sent
            NextState <= I2S_R_MSB_WAIT_FE;
          else
            NextState <= I2S_R_MSB_SEND_BITS;
        end
        
      I2S_R_MSB_WAIT_FE :
        begin
          if(I2cErrorDetect)
            //If we see an error we should terminate
            NextState <= I2S_ERROR;
          else if(SclFall)
            //Go handle LSB
            NextState <= I2S_R_LSB_LOAD;
          else
            NextState <= I2S_R_MSB_WAIT_FE;
        end
        
      I2S_R_LSB_LOAD :
        begin
          if(I2cErrorDetect)
            //If we see an error we should terminate
            NextState <= I2S_ERROR;
          else
            NextState <= I2S_R_LSB_CRC;
        end
      
      I2S_R_LSB_CRC :
        begin
          if(I2cErrorDetect)
            //If we see an error we should terminate
            NextState <= I2S_ERROR;
          else
            NextState <= I2S_R_LSB_SEND_BITS;
        end
      
      I2S_R_LSB_SEND_BITS :
        begin
          if(I2cErrorDetect)
            //If we see an error we should terminate
            NextState <= I2S_ERROR;
          else if(BitCnt == 4'd8)
            //Stay here until all 8 normal bits are sent
            NextState <= I2S_R_LSB_WAIT_FE;
          else
            NextState <= I2S_R_LSB_SEND_BITS;
        end

      I2S_R_LSB_WAIT_FE :
        begin
          if(I2cErrorDetect)
            //If we see an error we should terminate
            NextState <= I2S_ERROR;
             else if(SclFall & (CurrentByteId >= 8'd2))
            //Done with last requested word
             NextState <= I2S_R_CRC_L_LOAD;
          else if(SclFall)
            //More words to send
            NextState <= I2S_R_MSB_LOAD;
          else
            NextState <= I2S_R_LSB_WAIT_FE;
        end

      /////////////////////////////////////////////////////////////////////////////
      // CRC output for ending read
      /////////////////////////////////////////////////////////////////////////////
      
      I2S_R_CRC_M_LOAD :
        begin
          if(I2cErrorDetect)
            //If we see an error we should terminate
            NextState <= I2S_ERROR;
          else
            NextState <= I2S_R_CRC_M_SEND_BITS;
        end
      
      I2S_R_CRC_M_SEND_BITS :
        begin
          if(I2cErrorDetect)
            //If we see an error we should terminate
            NextState <= I2S_ERROR;
          else if(BitCnt == 4'd8)
            //Stay here until all 8 normal bits are sent
            NextState <= I2S_R_CRC_M_WAIT_FE;
          else
            NextState <= I2S_R_CRC_M_SEND_BITS;
        end
        
      I2S_R_CRC_M_WAIT_FE :
        begin
          if(I2cErrorDetect)
            //If we see an error we should terminate
            NextState <= I2S_ERROR;
          else if(SclFall)
            //Done
            NextState <= I2S_R_CRC_L_LOAD;
          else
            NextState <= I2S_R_CRC_M_WAIT_FE;
        end
      
      I2S_R_CRC_L_LOAD :
        begin
          if(I2cErrorDetect)
            //If we see an error we should terminate
            NextState <= I2S_ERROR;
          else
            NextState <= I2S_R_CRC_L_SEND_BITS;
        end
      
      I2S_R_CRC_L_SEND_BITS :
        begin
          if(I2cErrorDetect)
            //If we see an error we should terminate
            NextState <= I2S_ERROR;
          else if(BitCnt == 4'd8)
            //Stay here until all 8 normal bits are sent
            NextState <= I2S_R_CRC_L_WAIT_FE;
          else
            NextState <= I2S_R_CRC_L_SEND_BITS;
        end
        
      I2S_R_CRC_L_WAIT_FE :
        begin
          if(I2cErrorDetect)
            //If we see an error we should terminate
            NextState <= I2S_ERROR;
          else if(SclFall)
            //Done
            NextState <= I2S_IDLE;
          else
            NextState <= I2S_R_CRC_L_WAIT_FE;
        end
      
      /////////////////////////////////////////////////////////////////////////////
      // Error state for incomplete communication or CRC failure
      /////////////////////////////////////////////////////////////////////////////
        
      I2S_ERROR :
        //Flag error and go back to ready
        NextState <= I2S_IDLE;
        
      I2S_CRC_ERROR:
        //Flag error and go back to ready
        NextState <= I2S_W_CRC_L_ACK;
        
      default
        begin
          NextState <= I2S_IDLE;
        end
    endcase
  end
  
  //Decode state for control signals
  always @(CurrentState or CurrentByteVal)
  begin
    //Default to not asserted
    BytePrep      <= 1'b0;
    ByteStoreBits <= 1'b0;
    CheckAddress  <= 1'b0;
    ByteDone      <= 1'b0;
    CrcClr        <= 1'b0;
    NextSdaOut    <= 1'b1;
    avmm_write    <= 1'b0;
    NextI2cError  <= 1'b0;
    ByteLoaded    <= 1'b0;
    ByteLoadMsb   <= 1'b0;
    ByteLoadLsb   <= 1'b0;
    ByteLoadCrcM  <= 1'b0;
    ByteLoadCrcL  <= 1'b0;
    ByteReadBits  <= 1'b0;
    SetTimeoutCnt <= 1'b0;
    CrcMsbDone    <= 1'b0;
    WatchdogRst   <= 1'b0;
    CrcWait            <= 1'b0;
    set_NextCrcError   <= 1'b0;
    clear_NextCrcError <= 1'b0;
    avmm_read          <= 1'b1;
    
    case (CurrentState)
      I2S_IDLE :
        begin
          BytePrep              <= 1'b1;                      //Get ready for the next byte
          CrcClr                <= 1'b1;                      //Reset CRC calculation
          SetTimeoutCnt         <= 1'b1;                      //Prepare timer for new transfer
        end
        
      /////////////////////////////////////////////////////////////////////////////
      // Get the I2C Device Address Byte
      /////////////////////////////////////////////////////////////////////////////
        
      I2S_ADDR_GET_BITS :
        begin
          ByteStoreBits         <= 1'b1;                      //Store bits as they arrive
        end

      I2S_ADDR_CHK :
        begin
          CheckAddress          <= 1'b1;                      //See if the address matches our device address
        end

      I2S_ADDR_WAIT_FE :
        begin
          //Defaults OK
        end

      I2S_ADDR_ACK :
        begin
          NextSdaOut            <= 1'b0;                      //Pull low to give an I2C ACK
        end

      /////////////////////////////////////////////////////////////////////////////
      // Get the op bytes and all normal bytes
      /////////////////////////////////////////////////////////////////////////////
        
      I2S_W_BYTE_PREP :
        begin
          BytePrep              <= 1'b1;                      //Get ready for the next byte
        end

      I2S_W_BYTE_GET_BITS :
        begin
          ByteStoreBits         <= 1'b1;                      //Store bits as they arrive
        end

      I2S_W_BYTE_STORE :
        begin
          ByteDone              <= 1'b1;                      //Store finished byte
        end

      I2S_W_BYTE_WAIT_FE :
        begin
          //Defaults OK
        end

      I2S_W_BYTE_ACK :
        begin
          NextSdaOut            <= 1'b0;                      //Pull low to give an I2C ACK
        end
      /////////////////////////////////////////////////////////////////////////////
      // Switch to Read Branch
      ///////////////////////////////////////////////////////////////////////////// 
       I2S_A_RST :
        begin
        BytePrep              <= 1'b1;
        end
        
       I2S_A_GET_ADDR:
        begin
        ByteStoreBits         <= 1'b1;
        end
               
       I2S_A_ADDR_CHK :
        begin
        CheckAddress          <= 1'b1;
        end
               
       I2S_A_ADDR_WAIT_FE :
        begin
        //assert read
        avmm_read            <= 1'b1;    
        end
        
       I2S_A_ADDR_ACK:
        begin
        NextSdaOut            <= 1'b0;
        end
        
      /////////////////////////////////////////////////////////////////////////////
      // Get the CRC word
      /////////////////////////////////////////////////////////////////////////////
        
      I2S_W_CRC_M_PREP :
        begin
          BytePrep              <= 1'b1;                      //Get ready for the next byte
          clear_NextCrcError    <= 1'b1;
        end

      I2S_W_CRC_M_GET_BITS :
        begin
          ByteStoreBits         <= 1'b1;                      //Store bits as they arrive
        end

      I2S_W_CRC_M_STORE :
        begin
          CrcMsbDone            <= 1'b1;                      //Store finished byte from CRC
        end

      I2S_W_CRC_M_WAIT_FE :
        begin
          //Defaults OK
        end

      I2S_W_CRC_M_ACK :
        begin
          NextSdaOut            <= 1'b0;                      //Pull low to give an I2C ACK
        end

      I2S_W_CRC_L_PREP :
        begin
          BytePrep              <= 1'b1;                      //Get ready for the next byte
        end

      I2S_W_CRC_L_GET_BITS :
        begin
          ByteStoreBits         <= 1'b1;                      //Store bits as they arrive
        end

      I2S_W_CRC_L_WAIT_FE :
        begin
           CrcWait                       <= 1'b1;
        end

      I2S_W_CRC_L_ACK :
        begin
          NextSdaOut            <= 1'b0;                      //Pull low to give an I2C ACK
          WatchdogRst           <= 1'b1;                      //Write completed, reset watchdog
        end

      /////////////////////////////////////////////////////////////////////////////
      // Write data to register file
      /////////////////////////////////////////////////////////////////////////////
        
      I2S_W_REG_WR :
        begin
          avmm_write            <= 1'b1;                      //Write data to the Reg File
        end

      /////////////////////////////////////////////////////////////////////////////
      // Read operation - send two bytes per word
      /////////////////////////////////////////////////////////////////////////////
        
      I2S_R_MSB_LOAD :
        begin
          ByteLoadMsb           <= 1'b1;                      //Load byte for output
        end

      I2S_R_MSB_CRC :
        begin
          ByteLoaded            <= 1'b1;                      //Include byte in CRC calculation
        end

      I2S_R_MSB_SEND_BITS :
        begin
          ByteReadBits          <= 1'b1;                      //Allow bit to be clocked out
          NextSdaOut            <= CurrentByteVal[7];         //Output MSB first
        end

      I2S_R_MSB_WAIT_FE :
        begin
          //Defaults OK
        end

      I2S_R_LSB_LOAD :
        begin
          ByteLoadLsb           <= 1'b1;                      //Load byte for output
        end

      I2S_R_LSB_CRC :
        begin
          ByteLoaded            <= 1'b1;                      //Include byte in CRC calculation
        end

      I2S_R_LSB_SEND_BITS :
        begin
          ByteReadBits          <= 1'b1;                      //Allow bit to be clocked out
          NextSdaOut            <= CurrentByteVal[7];         //Output MSB first
        end

      I2S_R_LSB_WAIT_FE :
        begin
          //Defaults OK
        end

      /////////////////////////////////////////////////////////////////////////////
      // CRC output for ending read
      /////////////////////////////////////////////////////////////////////////////
      
      I2S_R_CRC_M_LOAD :
        begin
          ByteLoadCrcM          <= 1'b1;                      //Load MSB for output
        end

      I2S_R_CRC_M_SEND_BITS :
        begin
          ByteReadBits          <= 1'b1;                      //Allow bit to be clocked out
          NextSdaOut            <= CurrentByteVal[7];         //Output MSB first
        end

      I2S_R_CRC_M_WAIT_FE :
        begin
          //Defaults OK
        end

      I2S_R_CRC_L_LOAD :
        begin
          ByteLoadCrcL          <= 1'b1;                      //Load MSB for output
        end

      I2S_R_CRC_L_SEND_BITS :
        begin
          ByteReadBits          <= 1'b1;                      //Allow bit to be clocked out
          NextSdaOut            <= CurrentByteVal[7];         //Output MSB first
        end

      I2S_R_CRC_L_WAIT_FE :
        begin
          WatchdogRst           <= 1'b1;                      //Read completed, reset watchdog
        end

      /////////////////////////////////////////////////////////////////////////////
      // Error state for incomplete communication or CRC failure
      /////////////////////////////////////////////////////////////////////////////
        
      I2S_ERROR :
        begin
          NextI2cError          <= 1'b1;                      //Flag the error
        end
      
      I2S_CRC_ERROR :
        begin
          set_NextCrcError      <= 1'b1;
        end
      
    endcase
  end
  
  //Register decoded outputs
  always @(posedge Clock)
  begin
    if(Reset)
      begin
        SdaOut    <= 1'b1;
        I2cError  <= 1'b0;
      end
    else
      begin
        SdaOut    <= NextSdaOut;
        I2cError  <= NextI2cError;
      end
  end
    
endmodule
